package com.siyoumi.component.http;

import com.siyoumi.entity.SysAccsuperConfig;
import com.siyoumi.exception.BaseExceptionHandler;
import com.siyoumi.exception.XException;
import com.siyoumi.service.SysAccsuperConfigService;
import com.siyoumi.util.XJson;
import com.siyoumi.util.XLog;
import com.siyoumi.util.XReturn;
import com.siyoumi.util.XStr;
import com.siyoumi.util.entity.ThreadLocalData;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StreamUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.Map;
import java.util.function.Function;


@Slf4j
public class XHttpContext {
    private static String keyX = "__X__";
    public static String keyTokenData = "__TokenData__";


    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static HttpServletResponse getHttpServletResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }

    public static void del(String k) {
        ThreadLocalData.set(k, null);
    }

    public static void set(String k, Object v) {
        ThreadLocalData.set(k, v);
    }

    public static <T> T get(String k, T def) {
        if (XStr.isNullOrEmpty(k)) {
            return def;
        }

        return ThreadLocalData.get(k, def);
        //Object val = null;
        //try {
        //    val = getHttpServletRequest().getAttribute(k);
        //} catch (Exception ex) {
        //    log.error(ex.getMessage());
        //}
        //
        //if (val == null) {
        //    return def;
        //}
        //
        //return (T) val;
    }

    public static void setX(String x) {
        if (XStr.hasAnyText(x)) {
            set(keyX, x);
        }
    }

    public static String getX() {
        return getX(true);
    }

    /**
     * @param throwExc true：会抛出异常
     */
    public static String getX(Boolean throwExc) {
        String x = "";
        try {
            x = get(keyX, "");
            if (XStr.isNullOrEmpty(x)) {
                throw new XException("x is null");
            }
        } catch (Exception ex) {
            if (throwExc) {
                throw ex;
            }
        }

        return x;
    }

    public static SysAccsuperConfig getXConfig() {
        return getXConfig(true);
    }

    /**
     * 获取 x config
     *
     * @param getCache true：从redis中获取
     * @return x config
     */
    public static SysAccsuperConfig getXConfig(boolean getCache) {
        SysAccsuperConfigService app = SysAccsuperConfigService.getBean();
        return app.getXConfig(XHttpContext.getX(), getCache);
    }

    public static <T> T get(String k) {
        return get(k, null);
    }

    /**
     * 获取上下文内容，不存在设置
     */
    public static <R> R getAndSetData(String key, Function<String, R> func) {
        R entity = XHttpContext.get(key);
        if (entity == null) {
            entity = func.apply(key);
            if (entity != null) {
                XHttpContext.set(key, entity);
            }
        }

        return entity;
    }

    /**
     * 全路径，域名 + 路径 + 请求参数
     */
    public static String getUrlFull() {
        String url = getUrl();
        String query = getHttpServletRequest().getQueryString();
        if (XStr.hasAnyText(query)) {
            url = XStr.concat(url, "?", query);
        }
        return url;
    }

    /**
     * 域名 + 路径
     */
    public static String getUrl() {
        return XStr.concat(getRoot(), getUrlPath());
    }

    /**
     * 路径
     */
    public static String getUrlPath() {
        return getHttpServletRequest().getServletPath();
    }

    public static String getUrlPathAndQuery() {
        String path = getHttpServletRequest().getServletPath();
        String query = getHttpServletRequest().getQueryString();
        if (XStr.hasAnyText(query)) {
            path = XStr.concat(path, "?", query);
        }
        return path;
    }

    /**
     * 域名
     * https://siyoumic.om
     */
    public static String getRoot() {
        return XStr.concat(getScheme(), "://", getHttpServletRequest().getServerName());
    }

    public static String getScheme() {
        String scheme = getHttpServletRequest().getHeader("X-Forwarded-Proto");
        if (XStr.isNullOrEmpty(scheme)) {
            scheme = getHttpServletRequest().getScheme();
        }
        return scheme;
    }

    /**
     * 端口
     */
    public static Integer getPort() {
        return getHttpServletRequest().getServerPort();
    }


    /**
     * 获取token
     *
     * @return 获取令牌
     */
    public static String getToken() {
        String token = input("token");
        if (XStr.isNullOrEmpty(token)) {
            token = getHttpServletRequest().getHeader("token");
        }
        return token;
    }

    /**
     * 获取token
     *
     * @return 获取令牌
     */
    public static XReturn getTokenData() {
        return get(keyTokenData);
    }

    /**
     *
     */
    public static String getTokenDataType() {
        XReturn r = getTokenData();
        return r.getData("type", "");
    }


    public static String input(@NonNull String k) {
        return input(k, null);
    }

    public static String input(@NonNull String k, String def) {
        if (XStr.isNullOrEmpty(k)) {
            return def;
        }

        InputData inputData = InputAll();
        return inputData.input(k, def);
    }

    /**
     * 获取所有请求参数
     * 包含GET和POST请求参数
     */
    public static InputData InputAll() {
        String key = "XHttpContext:InputAll";
        InputData dataCache = XHttpContext.get(key);
        if (dataCache != null) {
            return dataCache;
        }

        InputData data = InputData.getIns();
        HttpServletRequest request = getHttpServletRequest();
        Enumeration<String> listKey = request.getParameterNames();
        while (listKey.hasMoreElements()) {
            String k = listKey.nextElement();
            String v = request.getParameter(k);
            data.put(k, v);
        }
        if (request.getMethod().equalsIgnoreCase("POST")) {
            //post请求
            String body = XHttpContext.getBody();
            if (XStr.hasAnyText(body)) {
                String contentType = request.getContentType().toLowerCase();
                log.debug("contentType: {}", contentType);
                switch (contentType) {
                    case "text/plain":
                    case "application/json":
                    case "application/json; charset=utf-8":
                        try {
                            Map mapBody = XJson.parseObject(body, Map.class);
                            mapBody.forEach((k, v) ->
                            {
                                if (v instanceof String) {
                                    data.put(k.toString(), v);
                                } else {
                                    data.put(k.toString(), XJson.toJSONString(v));
                                }
                            });
                        } catch (Exception e) {
                            BaseExceptionHandler.printStackTrace(e);
                            log.error(e.getMessage());
                        }
                        break;
                    case "application/x-www-form-urlencoded":
                        String[] bodyArr = body.split("&");
                        for (String s : bodyArr) {
                            String v = "";
                            String[] kv = s.split("=");
                            if (kv.length > 1) {
                                v = kv[1];
                            }
                            String kk = kv[0];
                            if (data.existsKey(kk)) {
                                //key重置，会输出['x','x']
                                continue;
                            }
                            data.put(kk, v);
                        }
                        break;
                }
            }
        }

        XHttpContext.set(key, data);
        return data;
    }

    @SneakyThrows
    public static String getBody() {
        byte[] body = StreamUtils.copyToByteArray(getHttpServletRequest().getInputStream());
        return new String(body);
    }

    /**
     * 获取应用ID
     */
    public static String getAppId() {
        String appId = input("app_id", "");
        if (XStr.hasAnyText(appId)) {
            return appId;
        }

        String url = getUrlPath();
        String[] urlData = url.split("/");
        if (urlData.length > 2) {
            appId = urlData[2];
        }

        return appId;
    }

    /**
     * 获取IP地址
     *
     * @return IP地址
     */
    public static String getIpAddr() {
        String root = XHttpContext.getRoot();
        if (root.toLowerCase().contains("localhost")) {
            return "127.0.0.1";
        }

        HttpServletRequest request = XHttpContext.getHttpServletRequest();

        String ip = null;
        try {
            ip = request.getHeader("X-Real-IP");
            if (XStr.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (XStr.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (XStr.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (XStr.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (XStr.isNullOrEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.error("{},{}", XHttpContext.class.getSimpleName(), e.getMessage());
        }

        return ip;
    }

}
